import * as CANNON from 'cannon-es';
import * as THREE from "three";
import { getSize } from './tooler';

export class Car {

    meshes: THREE.Object3D[] = [];

    constructor(physicsWorld: CANNON.World, bodyMesh: THREE.Mesh, wheelMesh: THREE.Mesh) {

        // this.resetMesh(bodyMesh);
        // this.resetMesh(wheelMesh);
        // bodyMesh.geometry.rotateY(Math.PI * 0.5);
        // bodyMesh.updateMatrix();

        setTimeout(() => {
            let bodySize = getSize(bodyMesh);
            let wheelSize = getSize(wheelMesh);
            console.log(bodySize, wheelSize)
            const groundMaterial = new CANNON.Material('ground');

            const planeShape = new CANNON.Plane()
            const planeBody = new CANNON.Body({ mass: 0, material: groundMaterial })
            planeBody.addShape(planeShape);

            planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI * 0.5);
            planeBody.position.set(0, -4, 0);

            physicsWorld.addBody(planeBody);

            // Build the car chassis
            const chassisShape = new CANNON.Box(new CANNON.Vec3(bodySize.x / 2, bodySize.y / 2, bodySize.z / 2))
            const chassisBody = new CANNON.Body({ mass: 150 })
            chassisBody.addShape(chassisShape)
            chassisBody.position.set(0, 4, 0)
            // chassisBody.angularVelocity.set(0, 0.5, 0)

            // demo.addVisual(chassisBody)
            // const boxGeometry = new THREE.BoxGeometry(4, 1, 2);
            // const boxMaterial = new THREE.MeshNormalMaterial();
            // const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
            // boxMesh.userData = { body: chassisBody }
            // this.meshes.push(boxMesh);
            bodyMesh.userData = { body: chassisBody };
            this.meshes.push(bodyMesh);

            // Create the vehicle
            const vehicle = new CANNON.RaycastVehicle({
                chassisBody,
                // indexForwardAxis: 0,
                // indexRightAxis: 1,
                // indexUpAxis: 2
            })

            const wheelOptions = {
                radius: 0.3,
                directionLocal: new CANNON.Vec3(0, -1, 0),
                suspensionStiffness: 30,
                suspensionRestLength: 0.3,
                frictionSlip: 1.4,
                dampingRelaxation: 2.3,
                dampingCompression: 4.4,
                maxSuspensionForce: 100000,
                rollInfluence: 0.01,
                axleLocal: new CANNON.Vec3(0, 0, 1),
                chassisConnectionPointLocal: new CANNON.Vec3(-1, 0, 1),
                maxSuspensionTravel: 0.3,
                customSlidingRotationalSpeed: -30,
                useCustomSlidingRotationalSpeed: true,
            }

            let zz = 0.72;
            let yy = -0.4;
            wheelOptions.chassisConnectionPointLocal.set(-1.32, yy, zz)
            vehicle.addWheel(wheelOptions)

            wheelOptions.chassisConnectionPointLocal.set(-1.32, yy, -zz)
            vehicle.addWheel(wheelOptions)

            wheelOptions.chassisConnectionPointLocal.set(1.12, yy, zz)
            vehicle.addWheel(wheelOptions)

            wheelOptions.chassisConnectionPointLocal.set(1.12, yy, -zz)
            vehicle.addWheel(wheelOptions)

            vehicle.addToWorld(physicsWorld);
            // physicsWorld.addBody(vehicle)

            // const wheels: any[] = [];

            // Add the wheel bodies
            const wheelBodies: any[] = []
            const wheelMaterial = new CANNON.Material('wheel')
            vehicle.wheelInfos.forEach((wheel) => {
                const cylinderShape = new CANNON.Cylinder(wheel.radius, wheel.radius, wheel.radius / 2, 20)
                const wheelBody = new CANNON.Body({
                    mass: 1,
                    material: wheelMaterial,
                })
                wheelBody.type = CANNON.Body.KINEMATIC
                wheelBody.collisionFilterGroup = 0 // turn off collisions
                const quaternion = new CANNON.Quaternion();
                quaternion.setFromEuler(-Math.PI / 2, 0, 0)
                wheelBody.addShape(cylinderShape, new CANNON.Vec3(), quaternion)
                wheelBodies.push(wheelBody)
                // demo.addVisual(wheelBody)
                physicsWorld.addBody(wheelBody);

                // let mesh = this.getWheelMesh(wheel.radius);
                let mesh = wheelMesh.clone();
                // stage.add(mesh);
                mesh.userData = { body: wheelBody };
                this.meshes.push(mesh);
                // wheels.push(mesh);

            })

            const wheel_ground = new CANNON.ContactMaterial(wheelMaterial, groundMaterial, {
                friction: 0.3,
                restitution: 0,
                contactEquationStiffness: 1000,
            })
            physicsWorld.addContactMaterial(wheel_ground)

            physicsWorld.addEventListener('postStep', () => {
                for (let i = 0; i < vehicle.wheelInfos.length; i++) {
                    vehicle.updateWheelTransform(i)
                    const transform = vehicle.wheelInfos[i].worldTransform
                    const wheelBody = wheelBodies[i]
                    wheelBody.position.copy(transform.position)
                    wheelBody.quaternion.copy(transform.quaternion)
                }
            })

            document.addEventListener('keydown', (event) => {
                const maxSteerVal = 0.5
                const maxForce = 400
                const brakeForce = 1000000

                switch (event.key) {
                    case 'w':
                    case 'ArrowUp':
                        vehicle.applyEngineForce(-maxForce, 2)
                        vehicle.applyEngineForce(-maxForce, 3)
                        break

                    case 's':
                    case 'ArrowDown':
                        vehicle.applyEngineForce(maxForce, 2)
                        vehicle.applyEngineForce(maxForce, 3)
                        break

                    case 'a':
                    case 'ArrowLeft':
                        vehicle.setSteeringValue(maxSteerVal, 0)
                        vehicle.setSteeringValue(maxSteerVal, 1)
                        break

                    case 'd':
                    case 'ArrowRight':
                        vehicle.setSteeringValue(-maxSteerVal, 0)
                        vehicle.setSteeringValue(-maxSteerVal, 1)
                        break

                    case 'b':
                        vehicle.setBrake(brakeForce, 0)
                        vehicle.setBrake(brakeForce, 1)
                        vehicle.setBrake(brakeForce, 2)
                        vehicle.setBrake(brakeForce, 3)
                        break
                }
            })

            // Reset force on keyup
            document.addEventListener('keyup', (event) => {
                switch (event.key) {
                    case 'w':
                    case 'ArrowUp':
                        vehicle.applyEngineForce(0, 2)
                        vehicle.applyEngineForce(0, 3)
                        break

                    case 's':
                    case 'ArrowDown':
                        vehicle.applyEngineForce(0, 2)
                        vehicle.applyEngineForce(0, 3)
                        break

                    case 'a':
                    case 'ArrowLeft':
                        vehicle.setSteeringValue(0, 0)
                        vehicle.setSteeringValue(0, 1)
                        break

                    case 'd':
                    case 'ArrowRight':
                        vehicle.setSteeringValue(0, 0)
                        vehicle.setSteeringValue(0, 1)
                        break

                    case 'b':
                        vehicle.setBrake(0, 0)
                        vehicle.setBrake(0, 1)
                        vehicle.setBrake(0, 2)
                        vehicle.setBrake(0, 3)
                        break
                }
            })
        }, 90);


    }

    resetMesh(item: any) {
        item.rotation.set(0, 0, 0);
        item.position.set(0, 0, 0);
        item.scale.set(1, 1, 1);
    }

    getWheelMesh(radius: number) {
        var obj = new THREE.Object3D();
        var geo = new THREE.CylinderGeometry(radius, radius, radius / 2, 20);
        var mat = new THREE.MeshLambertMaterial({ color: 0xff3333 });
        var mesh = new THREE.Mesh(geo, mat);
        obj.add(mesh);
        // var w = mesh.clone();
        mesh.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI / 2);
        // obj.add(mesh);
        return obj;
    }
}